#ifndef __database_h__
#define __database_h__

/* The MIT License

Copyright (c) 2007 Simon Kroeger (simonkroeger@gmx.de)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */

// TODO: transactions, queries without predefined result type

#include <vector>
#include <ostream>
#include <exception>
#include <boost/shared_ptr.hpp>
#include "sqlite3.h"
#include "../tuple/tuple.h"

namespace tbd {

typedef sqlite_int64 int64;
typedef std::vector<unsigned char> blob;

namespace sqlite_detail {

struct db_exception: public std::exception  {
  db_exception(const char *const& what): std::exception(what) {}
};

//////////////////////// db_value

struct db_null {};

template<class T> struct db_value {
  typedef T value_type;

  db_value(): _value(value_type()), _null(true) {}
  db_value(const value_type& value): _value(value), _null(false) {}
  db_value(const db_null&): _value(value_type()), _null(true) {}

  operator value_type& () { return _value; }
  operator const value_type& () const { return _value; }
  bool null() const { return _null; }
private:
  friend struct sqlite_stmt;
  value_type _value;
  bool _null;
};

template<> struct db_value<std::string> {
  typedef std::string value_type;

  db_value(): _value(value_type()), _null(true) {}
  db_value(const value_type& value): _value(value), _null(false) {}
  db_value(const char* value): _value(value), _null(false) {}
  db_value(const db_null&): _value(value_type()), _null(true) {}

  operator value_type& () { return _value; }
  operator const value_type& () const { return _value; }
  operator const char* () { return _value.c_str(); }
  bool null() { return _null; }
private:
  friend struct sqlite_stmt;
  value_type _value;
  bool _null;
};

template<class T> std::ostream& operator<<(std::ostream &os, db_value<T> v) {
  if (v.null()) return os << "NULL";
  else return os << ((T)v);
}

///////////////// SQLITE

struct sqlite_exception: public db_exception {
  sqlite_exception(sqlite3* db): 
    db_exception(sqlite3_errmsg(db)) {}

  sqlite_exception(sqlite3_stmt* stmt): 
    db_exception(sqlite3_errmsg(sqlite3_db_handle(stmt))) {}
};

struct sqlite {
  sqlite(const char* filename, bool readonly = false, bool create = true): _db(0) { 
#if (SQLITE_VERSION_NUMBER >= 3005000)
    int flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE;
    if (create && !readonly) flags |= SQLITE_OPEN_CREATE;
    if (!sqlite3_open_v2(filename, &_db, flags, 0)) return; 
#else
    if (!sqlite3_open(filename, &_db)) return;
#endif
    sqlite_exception e(_db);
    sqlite3_close(_db);
    throw e;
  }
  ~sqlite() { sqlite3_close(_db); }
  operator sqlite3* () { return _db; }

  int changes() { return sqlite3_changes(_db); }
  int64 rowid() { return sqlite3_last_insert_rowid(_db); }

  sqlite3_stmt* prepare(std::string sql) {
    sqlite3_stmt* stmt;
    if (sqlite3_prepare_v2(_db, sql.c_str(), 
      (int)sql.size(), &stmt, 0) == SQLITE_OK) return stmt;
    throw sqlite_exception(_db);
  }

private:
  sqlite3* _db;
};
typedef boost::shared_ptr<sqlite> sqlite_ptr;

struct sqlite_stmt {
  sqlite_stmt(sqlite_ptr db, const std::string sql):  
    _state(SQLITE_DONE), _stmt(db->prepare(sql)) {}

  template<class T> sqlite_stmt(sqlite_ptr db, const std::string sql, const T& x):  
    _state(SQLITE_DONE), _stmt(db->prepare(sql)) { bind(x); }

  void column(int& value, int i) { value = sqlite3_column_int(_stmt, i); }
  void column(int64& value, int i) { value = sqlite3_column_int64(_stmt, i); }
  void column(double& value, int i) { value = sqlite3_column_double(_stmt, i); }
  void column(std::string& value, int i) { 
    value = (const char*)sqlite3_column_text(_stmt, i); 
  }
  void column(blob& value, int i) { 
    const void* blob = sqlite3_column_blob(_stmt, i);
    int size = sqlite3_column_bytes(_stmt, i);
    value.resize(size);
    memcpy(&value[0], blob, size);
  }
  template<class T>void column(db_value<T>& value, int i) {
    if (sqlite3_column_type(_stmt, i) == SQLITE_NULL) {
      value._null = true;
      value._value = T();
    } else {
      value._null = false;
      column(value._value, i);
    }
  }

  void bind(const int& data, int i = 1) { 
    if (sqlite3_bind_int(_stmt, i, data) != SQLITE_OK) 
      throw sqlite_exception(_stmt);
  }
  void bind(const int64& data, int i = 1) { 
    if (sqlite3_bind_int64(_stmt, i, data) != SQLITE_OK) 
      throw sqlite_exception(_stmt);
  }
  void bind(const double& data, int i = 1) { 
    if (sqlite3_bind_double(_stmt, i, data) != SQLITE_OK) 
      throw sqlite_exception(_stmt);
  }
  void bind(const std::string& data, int i = 1) {
    if (sqlite3_bind_text(_stmt, i, data.c_str(), (int)data.size(), 
      SQLITE_TRANSIENT) != SQLITE_OK) throw sqlite_exception(_stmt);
  }
  void bind(const blob& data, int i = 1) {
    if (sqlite3_bind_blob(_stmt, i, &data[0], (int)data.size(), 
      SQLITE_TRANSIENT) != SQLITE_OK) throw sqlite_exception(_stmt);
  }
  void bind(const db_null&, int i = 1) {
    if (sqlite3_bind_null(_stmt, i) != SQLITE_OK) 
      throw sqlite_exception(_stmt);
  }

  void bind(const tuple_details::ignore&, int i = 1) {}

  template<class T> void bind(const T& x) {
    accept(x, bind_parameter(), this);
  }

  bool step() {
    do _state = sqlite3_step(_stmt); while (_state == SQLITE_BUSY);

    if (_state == SQLITE_ROW) return true;
    if (_state == SQLITE_DONE) return false;
    throw sqlite_exception(_stmt);
  }
  void reset() { 
    if (sqlite3_reset(_stmt) != SQLITE_OK) throw sqlite_exception(_stmt);
  }
  bool eod() { return _state == SQLITE_DONE; }

  std::string column_name(int i) const {
    return sqlite3_column_name(_stmt, i);
  }
  int column_count() {
    return sqlite3_column_count(_stmt);
  }

  ~sqlite_stmt() { sqlite3_finalize(_stmt); }
private:
  struct bind_parameter {
    template<class T> void operator() (const T& data, int i, sqlite_stmt* stmt) {
      stmt->bind(data, i + 1);
    }
  };

  sqlite3_stmt* _stmt;
  int _state;
};

typedef boost::shared_ptr<sqlite_stmt> stmt_ptr;

///////////////// Database

struct database {
  database(const char* filename): _db(new sqlite(filename)) {}

  int execute(std::string sql) {
    sqlite_stmt(_db, sql).step();
    return changes();
  }

  template<class T> int execute(std::string sql, const T& x) {
    sqlite_stmt(_db, sql, x).step();
    return changes();
  }

  int changes() { return _db->changes(); }
  int64 rowid() { return _db->rowid(); }
private:
  friend struct statement;
  operator sqlite_ptr () { return _db; }
  database(): _db((sqlite*)0) {}
  sqlite_ptr _db;
};

statement null_statement();

///////////////// Statement

struct statement {
  statement(database db, const std::string sql):  
    _db(db), _stmt(new sqlite_stmt(db, sql)) {}

  template<class T> statement(database db, const std::string sql, const T& x):  
    _db(db), _stmt(new sqlite_stmt(db, sql, x)) {}

  bool operator==(const statement& x) const { 
    return (_stmt.get() == x._stmt.get());
  }

  template<class T> void column(T& value, int i) { _stmt->column(value, i); }
  template<class T> void bind(const T& data, int i) { _stmt->bind(data, i); }
  template<class T> void bind(const T& data) { _stmt->bind(data); }

  std::string column_name(int i) const { return _stmt->column_name(i); }
  int column_count() { return _stmt->column_count(); }

  bool step() { return _stmt->step(); }
  void reset() { _stmt->reset(); }
  bool eod() const { return !_stmt.get() || _stmt->eod(); }
  database db() const { return _db; }
private:
  friend statement null_statement();
  statement(): _stmt((sqlite_stmt*)0) {}

  stmt_ptr _stmt;
  database _db;
};

statement null_statement() { return statement(); }

///////////////// Command

struct executer {};

template<class T> struct bound_executer {
  bound_executer(const T& value): _value(value) {}
  const T& value() const { return _value; }
protected:
  const T& _value;
};

struct command {
  command(database db, string sql):
    _stmt(db, sql) {}

  template<class P> command(database db, string sql, const P& params):
    _stmt(db, sql, params) {
  }

  template<class P> command& bind(const P& params) {
    _stmt.bind(params);
    return *this;
  }

  template<class T> command& bind(const T& t, int i) {
    _stmt.bind(t, i);
    return *this;
  }

  template<class P> int execute(const P& params) {
    _stmt.bind(params);
    _stmt.step();
    _stmt.reset();
    return db().changes();
  }

  int execute() {
    _stmt.step();
    _stmt.reset();
    return db().changes();
  }
  database db() { return _stmt.db(); }
protected:
  statement _stmt;
};

struct command_binder {
  command_binder(command& cmd): _cmd(cmd), _next(1) {}
  template<class T> command_binder& operator| (const T& t) {
    _cmd.bind(t, _next++);
    return *this;
  }

  template<class T> command& operator| (const bound_executer<T>& t) {
    _cmd.bind(t.value(), _next++).execute();
    return _cmd;
  }
  
  command& operator| (const executer&) {
    _cmd.execute();
    return _cmd;
  }
protected:
  command& _cmd;
  int _next;
};

template<class T> command_binder operator << (command& cmd, const T& t) {
  return command_binder(cmd) | t;
}

template<class T> bound_executer<T> operator << (const T& t, const executer&) {
  return bound_executer<T>(t);
}

///////////////// Iterator

template <class T> struct iterator {
  typedef typename iterator<T> self;
  typedef typename T value_type;
  typedef typename value_type& reference;
  typedef typename boost::shared_ptr<value_type> pointer;
  typedef typename std::input_iterator_tag iterator_category;
  typedef typename int difference_type;

  iterator(statement stmt): _stmt(stmt), _value(new value_type()) {
    reset();
  }

  static const self& end() {
    static const self end;
    return end;
  }

  self& operator++() {
    if (_stmt.step()) _value->accept(from_stmt(_stmt));
    return *this; 
  }

  self& reset() {
    _stmt.reset();
    return ++(*this);
  }

  bool operator==(const self& other) const { 
    return (_stmt == other._stmt) || (eod() && other.eod());
  }

  bool operator!=(const self& x) const { return !(*this == x); }

  value_type& operator*() { return *_value; }
  const value_type& operator*() const { return *_value; }

  template<class P> void bind(const P& params) { _stmt.bind(params); }

  bool eod() const { return _stmt.eod(); }
  database db() const { return _stmt.db(); }
private: 
  iterator(): _stmt(null_statement()), _value() {}

  struct from_stmt {
    from_stmt(statement& stmt): _stmt(stmt) {}
    template<class T>void operator () (T& value, int i) const {
      _stmt.column(value, i);
    }
  private:
    statement& _stmt;
  };

  pointer _value;
  statement _stmt;
};

///////////////// Query

struct query_binder {
  query_binder(statement stmt): _stmt(stmt), _next(1) {}

  template<class T> query_binder& operator| (const T& t) {
    _stmt.bind(t, _next++);
    return *this;
  }
protected:
  statement _stmt;
  int _next;
};

struct column_iterator {
  typedef std::string value_type;
  typedef value_type& reference;
  typedef value_type* pointer;
  typedef std::input_iterator_tag iterator_category;
  typedef int difference_type;

  column_iterator(statement stmt, int index = 0): _stmt(stmt), _index(index) {}

  column_iterator& operator++() {
    ++_index;
    return *this; 
  }

  value_type operator*() const { return _stmt.column_name(_index); }

  bool operator==(const column_iterator& other) const { 
    return (_stmt == other._stmt) && (_index == other._index);
  }

  bool operator!=(const column_iterator& x) const { return !(*this == x); }

private:
  statement _stmt;
  int _index;
};

struct stmt_columns {
  typedef column_iterator const_iterator;
  stmt_columns(statement stmt): _stmt(stmt) {}
  column_iterator begin() { return column_iterator(_stmt); }
  column_iterator end() { return column_iterator(_stmt, _stmt.column_count()); }
private:
  statement _stmt;
};

template<TBD_TUPLE_ENUM_PARAMS_WITH_DEFAULTS(class S, = void)> struct query {
  typedef typename tuple<TBD_TUPLE_ENUM_PARAMS(S)> row;
  typedef iterator<const row> const_iterator;
  typedef iterator<row> iterator;
  typedef stmt_columns columns_type;

  query(database db, string sql):
    _stmt(db, sql) {}

  template<class P> query(database db, string sql, const P& params):
    _stmt(db, sql, params) {}

  template<class P> void bind(const P& params) {
    _stmt.bind(params);
  }

  iterator begin() {
    return iterator(_stmt);
  }

  static const iterator& end() {
    return iterator::end();
  }
  
  columns_type columns() { return columns_type(_stmt); }

  std::string column_name(int i) { return _stmt.column_name(i); }
  int column_count() { return _stmt.column_count(); }

  std::vector<std::string> column_names() {
    std::vector<std::string> names(row::size);
    for (int i = 0; i < row::size; ++i) names.at(i)= column_name(i);
    return names;
  }

  template<class T> query_binder operator << (const T& t) {
    return query_binder(_stmt) | t;
  }

  bool eod() const { return _stmt.eod(); }
  database db() { return _stmt._db(); }
protected:
  statement _stmt;
};


} // namespace detail

using sqlite_detail::query;
using sqlite_detail::command;
using sqlite_detail::db_value;
using sqlite_detail::database;
using sqlite_detail::db_exception;

typedef db_value<int> db_int;
typedef db_value<int64> db_int64;
typedef db_value<double> db_double;
typedef db_value<std::string> db_string;

const sqlite_detail::db_null null = sqlite_detail::db_null();
const sqlite_detail::executer exec = sqlite_detail::executer();

} // namespace tbd

#endif // __database_h__